#include "SKBookLibrary.hpp"

void getNotified(BookId bookId)
{
    cout << "Book [" << bookId << "] is available currently" << endl;
}


void* addToBooks(void* args)
{
    int threadId = *(int*)args;
    const char* authors[] = {"John", "Peter", "Thomas", "Jacob", "Siril"};
    const char* category[] = {"Fiction", "Comedy", "Tragic", "Science", "Horror"};
    SKBookLibrary& myLibrary = SKBookLibrary::getInstance();

    if (threadId == 0)
    {
	for (unsigned long i = 453627; i < 458627; ++i)
	{
	    SKBookNode tmp(i, authors[i % 5], time(NULL), category[i % 5], 12.45);
	    cout << "Thread [" << threadId << "] will be adding record for bookId [" << tmp.bookId_ << "]" << endl;
	    myLibrary.AddBook(&tmp);
	}
    }
    else
    {
	for (unsigned long i = 553627; i < 558627; ++i)
	{
	    SKBookNode tmp(i, authors[i % 5], time(NULL), category[i % 5], 12.45);
	    cout << "Thread [" << threadId << "] will be adding record for bookId [" << tmp.bookId_ << "]" << endl;
	    myLibrary.AddBook(&tmp);
	}
    }
}

void* removeBooks(void* args)
{
    int threadId = *(int*)args;

    SKBookLibrary& myLibrary = SKBookLibrary::getInstance();

    if (threadId == 0)
    {
	for (unsigned long i = 554627; i < 556627; ++i)
	{
	    cout << "Thread [" << threadId << "] will be removing record for bookId [" << i << "]" << endl;
	    myLibrary.removeBook(i);
	}
    }
    else
    {
	for (unsigned long i = 453527; i < 454627; ++i)
	{
	    cout << "Thread [" << threadId << "] will be removing record for bookId [" << i << "]" << endl;
	    myLibrary.removeBook(i);
	}
    }
}

void* getBooks(void* args)
{
    int threadId = *(int*)args;

    SKBookLibrary& myLibrary = SKBookLibrary::getInstance();

    if (threadId == 0)
    {
        for (unsigned long i = 453630; i < 458600; ++i)
        {
            cout << "Thread [" << threadId << "] will retrieve book for bookId [" << i << "]" << endl;
            myLibrary.getBook(i);
        }
    }
    else
    {
        for (unsigned long i = 553630; i < 559627; ++i)
        {
            cout << "Thread [" << threadId << "] will retrieve book for bookId [" << i << "]" << endl;
            myLibrary.getBook(i);
        }
    }
}

void* collectBooks(void* args)
{
    int threadId = *(int*)args;

    SKBookLibrary& myLibrary = SKBookLibrary::getInstance();

    if (threadId == 0)
    {
        for (unsigned long i = 453630; i < 458600; ++i)
        {
            cout << "Thread [" << threadId << "] will give back book for bookId [" << i << "]" << endl;
            myLibrary.collectBook(i);
        }
    }
    else
    {
        for (unsigned long i = 553630; i < 558627; ++i)
        {
            cout << "Thread [" << threadId << "] will give back book for bookId [" << i << "]" << endl;
            myLibrary.collectBook(i);
        }
    }
}

int main()
{
    const char* authors[] = {"Siril", "Anu", "Varghese", "Kuriakose"};
    const char* category[] = {"Horror", "Comedy", "Silent", "Terror"};
    SKBookLibrary& myLibrary = SKBookLibrary::getInstance();

    if (!myLibrary.initialize())
    {
	cerr << "Couldn't initialize the library. Testing failed." << endl;
	return -1;
    }

    for (unsigned long i = 213435; i < 213535; ++i)
    {
	SKBookNode tmp(i, authors[i % 4], time(NULL), category[i % 4], 23.56);
	myLibrary.AddBook(&tmp);
    }

    
    if (myLibrary.isAvailableBookId(213437))
	cout << "book [" << 213435 << "] is available" << endl;
    
    if (!myLibrary.isAvailableBookId(21345))
	cout << "book [" << 21345 << "] is not available" << endl;

    for (unsigned long i = 213436; i < 213530; ++i)
    {
	myLibrary.removeBook(i);
    }

    myLibrary.showAllBooks();
    
    myLibrary.displayBooksByAuthor("Siril");
    myLibrary.displayBooksByCategory("Silent");

    myLibrary.notifyOnAvailability(245637, getNotified);

    {
	SKBookNode tmp(245637, authors[245637 % 4], time(NULL), category[245637 % 4], 29.56);
	myLibrary.AddBook(&tmp);
    }

    myLibrary.notifyOnAvailability(245637, getNotified);
    myLibrary.getBook(245637);
    myLibrary.notifyOnAvailability(245637, getNotified);

    myLibrary.collectBook(245637);

    // testing with multiple threads
    pthread_t addThread[2];
    pthread_t deleteThread[2];
    pthread_t getBookThread[2];
    pthread_t collectBookThread[2];

    for (int i = 0; i < 2; ++i)
    {
        pthread_create(&addThread[i], NULL, addToBooks, &i);
        pthread_join(addThread[i], NULL);
    }

    for (int i = 0; i < 2; ++i)
    {
        pthread_create(&deleteThread[i], NULL, removeBooks, &i);
        pthread_join(deleteThread[i], NULL);
    }

    for (int i = 0; i < 2; ++i)
    {
        pthread_create(&getBookThread[i], NULL, getBooks, &i);
        pthread_join(getBookThread[i], NULL);
    }

    for (int i = 0; i < 2; ++i)
    {
        pthread_create(&collectBookThread[i], NULL, collectBooks, &i);
        pthread_join(collectBookThread[i], NULL);
    }



    return 0;
}
